 /* 
 * Copyright (c) 2008 by James Bremner
 * All rights reserved.
 *
 * Use license: Modified from standard BSD license.
 * 
 * Redistribution and use in source and binary forms are permitted
 * provided that the above copyright notice and this paragraph are
 * duplicated in all such forms and that any documentation, advertising 
 * materials, Web server pages, and other materials related to such
 * distribution and use acknowledge that the software was developed
 * by James Bremner. The name "James Bremner" may not be used to 
 * endorse or promote products derived from this software without 
 * specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */


#include "stdafx.h"
#include "cWebem.h"
#include <boost/bind.hpp>
#include <boost/lexical_cast.hpp>
#include <libs/asio/example/http/server/reply.hpp>
#include <libs/asio/example/http/server/request.hpp>

namespace http {
	namespace server {

	typedef std::multimap  < std::string, std::string> webem_mmp_name_value;
	typedef std::multimap  < std::string, std::string>::iterator webem_iter_name_value;

/**

Webem constructor

@param[in] address  e.g. "0.0.0.0"
@param[in] port     e.g. "8080"
@param[in[ doc_root path to folder containing html e.g. "./"

*/
cWebem::cWebem( 
	   const std::string& address,
	   const std::string& port,
	   const std::string& doc_root ) :
myRequestHandler( doc_root ),
myServer( address, port, myRequestHandler )
{
	myRequestHandler.setWebem( this );
}


	/**

	Create a link between a string ID and a function to calculate the dynamic content of the string

	@param[in] idname  string identifier
  @param[in] fun pointer to function which calculates the string to be displayed

*/

void cWebem::RegisterIncludeCode( const char* idname, webem_include_function fun )
{
	myIncludes.insert( std::pair<std::string, webem_include_function >( std::string(idname), fun  ) );
}

void cWebem::RegisterActionCode( const char* idname, webem_action_function fun )
{
	myActions.insert( std::pair<std::string, webem_action_function >( std::string(idname), fun  ) );
}

/**

  Include generated text

  @param[in/out] reply  text to include generated

  The text is searched for "<!--#cWebemX-->".  
  The X can be any string not containing "-->"
  If X has been registered with cWebem them the associated function
  is called to generate text to be inserted.

*/
void cWebem::Include( std::string& reply )
{
	int p = 0;
	while( 1 ) {
		// find next request for generated text
		p = reply.find("<!--#webem",p);
		if( p == -1 ) {
			break;
		}
		int q = reply.find("-->",p);
		if( q == -1 )
			break;;
		q += 3;

		int reply_len = reply.length();

		// code identifying text generator
		std::string code = reply.substr( p+11, q-p-15 );

		// find the function associated with this code
		std::map < std::string, webem_include_function >::iterator pf = myIncludes.find( code );
		if( pf != myIncludes.end() )
			// insert generated text
			reply.insert( p, pf->second() );

		// adjust pointer into text for insertion
		p = q + reply.length() - reply_len;
	}
}
/**

Is this a request from a button clicked on a form with a cWebem action

*/
void cWebem::CheckForAction( std::string& uri )
{
	// look for cWebem form action request
	int q = uri.find(".webem?");
	if( q == -1 )
		return;

	// find function matching action code
	std::string code = uri.substr(1,q-1);
	std::map < std::string, webem_action_function >::iterator
		pfun = myActions.find(  code );
	if( pfun == myActions.end() )
		return;

	// decode the values
	
	myNameValues.clear();
	std::string name;
	std::string value;

	int p = q + 7;
	int flag_done = 0;
	while( ! flag_done ) {
		q = uri.find("=",p);
		name = uri.substr(p,q-p);
		p = q + 1;
		q = uri.find("&",p);
		if( q != -1 )
			value = uri.substr(p,q-p);
		else {
			value = uri.substr(p);
			flag_done = 1;
		}
		// the browser sends blanks as +
		while( 1 ) {
			int p = value.find("+");
			if( p == -1 )
				break;
			value.replace( p, 1, " " );
		}

		myNameValues.insert( std::pair< std::string,std::string > ( name, value ) );
		p = q+1;
	}

	// call the function
	uri = pfun->second( this );

	return;
}
/**

  Find the value of a name set by a form submit action

*/
std::string& cWebem::FindValue( const char* name )
{
	static string ret;
	webem_iter_name_value iter = myNameValues.find( name );
	if( iter != myNameValues.end() )
		ret = iter->second;
	
	return ret;
}

void cWebemRequestHandler::handle_request( const request& req, reply& rep)
{
	// check for webem action request
	request req_modified = req;
	myWebem->CheckForAction( req_modified.uri );

	// do normal handling
	request_handler::handle_request( req_modified, rep);

	// Find and include any special cWebem strings
	myWebem->Include( rep.content );

	// adjust content length header
	// ( Firefox ignores this, but apparently some browsers truncate display without it.
	// fix provided by http://www.codeproject.com/Members/jaeheung72 )

	rep.headers[0].value = boost::lexical_cast<std::string>(rep.content.size());

}

	}
}

